package com.itheima.utils;

import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 操作Redis的String数据结构,设置值
     * @param key
     * @param value
     */
    public void set(Object key,Object value){
        if (key == null || (key instanceof String && StringUtils.isEmpty(key))){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
        redisTemplate.boundValueOps(key).set(value);
    }

    public Object get(String key) {
        //RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     *  操作String数据结构设置值,带失效时间
     * @param key
     * @param value
     * @param expire 有效时间(秒)
     */
    public void set(Object key,Object value,long expire,TimeUnit timeUnit){
        if (key == null || (key instanceof String && StringUtils.isEmpty(key))){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
        redisTemplate.boundValueOps(key).set(value,expire,timeUnit);
    }

    /**
     * redis计数器, 原子递减
     * @param key
     * @return 剩余数量
     */
    public Long decrement(Object key){
        if (key == null || (key instanceof String && StringUtils.isEmpty(key))){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
        return redisTemplate.opsForValue().decrement(key);
    }

    /**
     * redis计数器, 原子递增
     * @param key
     * @return 执行命令后value的数量
     */
    public Long increment(Object key){
        if (key == null || (key instanceof String && StringUtils.isEmpty(key))){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
        return redisTemplate.opsForValue().increment(key);
    }

    public Boolean expire(Object key,long expire,TimeUnit timeUnit){
        if (key == null || (key instanceof String && StringUtils.isEmpty(key))){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
       return redisTemplate.boundValueOps(key).expire(expire,timeUnit);
    }

    /**
     * 分布式锁
     */
    public Boolean setnx(Object key, Object value){
        if (key == null || (key instanceof String && StringUtils.isEmpty(key))){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }
    /**
     * 分布式锁,带失效时间
     */
    public Boolean setnx(Object key, Object value,long expire, TimeUnit timeUnit){
        if (key == null || (key instanceof String && StringUtils.isEmpty(key))){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
        return redisTemplate.opsForValue().setIfAbsent(key,value,expire,timeUnit);
    }

    /**
     * 操作Redis的Hash数据结构
     * @param bigKey Redis的大key
     * @param hashKey Redis的小key
     * @param hashValue Redis的Value值
     */
    public void hashSet(Object bigKey,Object hashKey,Object hashValue){
        if (bigKey == null || hashKey == null ){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
        if ((bigKey instanceof String) && StringUtils.isEmpty(bigKey) ||
                (hashKey instanceof String) && StringUtils.isEmpty(hashKey)){
            throw new RuntimeException("The Redis Key is Empty");
        }
        redisTemplate.boundHashOps(bigKey).put(hashKey,hashValue);
    }

    /**
     * 操作Redis的Hash数据结构 (带失效时间)
     * @param bigKey Redis的大key
     * @param hashKey Redis的小key
     * @param hashValue Redis的Value值
     */
    public void hashSet(Object bigKey,Object hashKey,Object hashValue,Long expire,TimeUnit timeUnit){
        if (bigKey == null || hashKey == null ){
            throw new RuntimeException("The Redis Key is can`t be null");
        }
        if ((bigKey instanceof String) && StringUtils.isEmpty(bigKey) ||
                (hashKey instanceof String) && StringUtils.isEmpty(hashKey)){
            throw new RuntimeException("The Redis Key is Empty");
        }
        BoundHashOperations hashOps = redisTemplate.boundHashOps(bigKey);
        hashOps.put(hashKey,hashValue);
        hashOps.expire(expire,timeUnit);
    }


    /**
     * 根据key获取value, String数据结构
     */
    public <T> T get(Object key, Class<T> clazz){
        if (key == null) {
            throw new RuntimeException("The Redis Key is Empty");
        }
         return (T) redisTemplate.opsForValue().get(key);
    }

    /**
     * 根据key获取value, Hash数据结构
     */
    public <T> T get(Object bigKey, Object hashKey, Class<T> clazz){
        if (bigKey == null || hashKey == null) {
            throw new RuntimeException("The Redis Key is Empty");
        }
        return (T) redisTemplate.boundHashOps(bigKey).get(hashKey);
    }

    /**
     * 根据key删除
     */
    public void delete(Object key){
        redisTemplate.delete(key);
    }

    /**
     * 向zset数据结构中设置值
     */
    public boolean zset(Object key, Object value, Long score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 根据设置的score获取区间值 (zset数据结构)
     * @param key
     * @param min
     * @param max
     */
    public Set<Object> rangeByScore(Object key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 根据设置的score获取区间值从给定下标和给定长度获取最终值
     * @param min score起始值
     * @param max score截止值
     * @param offset 下标
     * @param count 长度（个数）
     * @return
     */
    public Set rangeByScore(Object key, double min, double max,long offset, long count) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
    }

    /**
     * 按分值正序取出第一个
     * @param key
     */
    public Set<ZSetOperations.TypedTuple> rangeWithScore(Object key) {
        return redisTemplate.opsForZSet().rangeWithScores(key, 0, 0);
    }


    public Set<ZSetOperations.TypedTuple> zrangeWithScore(Object key) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, 0);
    }

    /**
     * (zset数据结构) 移除键值
     * @param key
     * @param values
     * @return 删除的个数
     */
    public Long remove(Object key, Object... values) {
        return redisTemplate.opsForZSet().remove(key, values);
    }
}
